#pragma once
#include <string>
#include <memory>
#include <poll.h>
#include "InetAddr.hpp"
#include "log.hpp"
#include "socket.hpp"
#include "LockGuard.hpp"
using namespace socket_ns;
//
class poll_server
{
    const static int gdefaultfd = -1; // 初始文件描述符值
    const static int gnum = 1024;     // 默认描述符集大小
    const static int gdefaultevent = 8;

public:
    poll_server(uint16_t port)
        : _port(port), _listen_sock(std::make_unique<tcp_socket>()), _isrunning(false), _timeout(-1)
    {
        InetAddr addr("0", _port);
        _listen_sock->build_server_socket(addr);
        for (int i = 0; i < gnum; i++)
        {
            _events[i].fd = gdefaultfd;
            _events[i].events = gdefaultevent;
            _events[i].revents = gdefaultevent;
        }
        _events[0].fd = _listen_sock->SockFd();
        _events[0].events = POLLIN; // 表示对读事件关系(listensock有连接，就是读事件就绪)
        LOG(DEBUG, "add listen socket fd is: %d\n", _listen_sock->SockFd());
    }

    void accept_client()
    {
        InetAddr client_addr;
        int sockfd = _listen_sock->accepter(&client_addr); // 此时不会阻塞，因为已经有客户端发起连接请求了
        if (sockfd < 0)
        {
            return;
        }
        LOG(DEBUG, "Get new Link, sockfd: %d, client info %s:%d\n", sockfd, client_addr.Ip().c_str(), client_addr.Port());
        int pos = 1;
        // 从小的位置开始放文件描述符
        for (; pos < gnum; pos++)
        {
            if (_events[pos].fd == gdefaultfd)
                break;
        }
        if (pos == gnum)
        {
            ::close(sockfd);
            LOG(WARNING, "server is full! \n");
            return;
        }
        else
        {
            _events[pos].fd = sockfd;
            _events[pos].events = POLLIN;
            _events[pos].revents = gdefaultevent;
            LOG(DEBUG, "%d add to select array!\n", sockfd);
        }
        LOG(DEBUG, "curr fd_array[] fd list : %s\n", rfds_to_string().c_str());
    }
    void service_io(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(_events[pos].fd, buffer, sizeof(buffer) - 1, 0); // 读取文件描述符内容，不会阻塞，文件内有内容了
        if (n > 0)                                                          // 读取成功
        {
            buffer[n] = 0;
            std::cout << "client say:" << buffer << std::endl;
            std::string echo_string = "[sercer echo]# ";
            echo_string += buffer;
            ::send(_events[pos].fd, echo_string.c_str(), echo_string.size(), 0);
        }
        else if (n == 0) // 文件描述符关闭
        {
            LOG(DEBUG, "%d is closed\n", _events[pos].fd);
            ::close(_events[pos].fd);
            _events[pos].fd = gdefaultfd;
            _events[pos].events = _events[pos].revents = gdefaultevent;
            LOG(DEBUG, "curr fd_array[] fd list : %s\n", rfds_to_string().c_str());
        }
        else // 错误
        {
            LOG(DEBUG, "%d is closed\n", _events[pos].fd);
            ::close(_events[pos].fd);
            _events[pos].fd = gdefaultfd;
            _events[pos].events = _events[pos].revents = gdefaultevent;
            LOG(DEBUG, "curr fd_array[] fd list : %s\n", rfds_to_string().c_str());
        }
    }
    void handler_event()
    {
        for (int i = 0; i < gnum; i++)
        {
            if (_events[i].fd == gdefaultfd)
                continue;
            int fd = _events[i].fd;
            short revents = _events[i].revents;
            // 多种情况会导致socket 就绪：需要辨别
            if (revents & POLLIN) // 判断revents 是否设置了POLLIN标志位，设置表示文件描述符已经准备好进行读取操作
            {
                if (_events[i].fd == _listen_sock->SockFd()) // 等于监听套接字时，此时有客户端发起连接请求
                {
                    accept_client();
                }
                else //_fd_array[i]文件描述符中可以读取该文件描述符了
                {
                    service_io(i);
                }
            }
            else if (revents & POLLOUT) // POLLOUT标志位用于指示一个文件描述符已经准备好进行写操作
            {
                // TODO
            }
        }
    }
    void loop()
    {
        _isrunning = true;
        while (true)
        {
            // struct timeval
            // {
            //     long tv_sec;  // 秒数
            //     long tv_usec; // 微秒数
            // };
            int n = poll(_events, gnum, _timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "poll timeout...\n");
                break;
            case -1:
                LOG(ERROR, "select error...\n");
                break;
            default:
                LOG(DEBUG, "Event Happen. n : %d\n", n);
                handler_event(); // 处理事件
                break;
            }
        }
    }
    std::string rfds_to_string()
    {
        std::string fdstr;
        for (int i = 0; i < gnum; i++)
        {
            if (_events[i].fd == gdefaultfd)
            {
                continue;
            }
            fdstr += std::to_string(_events[i].fd);
            fdstr += " ";
        }
        return fdstr;
    }
    ~poll_server()
    {
    }

private:
    uint16_t _port;                         // 端口号
    std::unique_ptr<mysocket> _listen_sock; // 套接字
    struct pollfd _events[gnum];            // 辅助数组
    bool _isrunning;
    int _timeout;
};
